In [1]:
from notebook_preamble import J, V
This is what I like to call the functions that just rearrange things on the stack. (One thing I want to mention is that during a hypothetical compilation phase these "stack chatter" words effectively disappear, because we can map the logical stack locations to registers that remain static for the duration of the computation. This remains to be done but it's "off the shelf" technology.)
In [2]:
J('1 2 3 clear')
In [3]:
J('1 2 3 dup')
In [4]:
J('1 2 3 dupd')
In [5]:
J('1 2 3 enstacken') # Replace the stack with a quote of itself.
In [6]:
J('4 5 6 [3 2 1] disenstacken') # Unpack a list onto the stack.
In [7]:
J('1 2 3 stack') # Get the stack on the stack.
In [8]:
J('1 2 3 [4 5 6] unstack') # Replace the stack with the list on top.
# The items appear reversed but they are not,
# 4 is on the top of both the list and the stack.
In [9]:
J('1 2 3 pop')
In [10]:
J('1 2 3 popd')
In [11]:
J('1 2 3 popop')
In [12]:
J('1 2 3 roll<')
In [13]:
J('1 2 3 roll>')
In [14]:
J('1 2 3 swap')
In [15]:
J('1 2 3 tuck')
In [16]:
J('1 2 3 over')
In [17]:
J('1 2 3 unit')
In [18]:
J('1 2 3 quoted')
In [19]:
J('1 [2] 3 unquoted')
In [20]:
V('1 [dup] 3 unquoted') # Unquoting evaluates. Be aware.
In [21]:
J('[1 2 3] [4 5 6] concat')
In [22]:
J('[1 2 3] [4 5 6] swoncat')
In [23]:
J('[1 2 3] [4 5 6] shunt')
In [24]:
J('1 [2 3] cons')
In [25]:
J('[2 3] 1 swons')
In [26]:
J('[1 2 3] uncons')
In [27]:
J('[1 2 3 4] first')
In [28]:
J('[1 2 3 4] second')
In [29]:
J('[1 2 3 4] third')
In [30]:
J('[1 2 3 4] rest')
In [31]:
J('[[1] [2 [3] 4] [5 6]] flatten')
In [32]:
J('[10 11 12 13 14] 2 getitem')
In [33]:
J('[1 2 3 4] 0 at')
In [34]:
J('2 [1 2 3 4] of')
In [35]:
J('[1 2 3 4] 2 drop')
In [36]:
J('[1 2 3 4] 2 take') # reverses the order
reverse
could be defines as reverse == dup size take
In [37]:
J('[1 2 3 1 4] 1 remove')
In [38]:
J('[1 2 3 4] reverse')
In [39]:
J('[1 1 1 1] size')
In [40]:
J('1 2 3 [4 5 6] swaack')
In [41]:
J('23 9 1 choice')
In [42]:
J('23 9 0 choice')
In [43]:
J('[23 9 7] 1 select') # select is basically getitem, should retire it?
In [44]:
J('[23 9 7] 0 select')
In [45]:
J('[1 2 3] [6 5 4] zip')
In [46]:
J('[1 2 3] [6 5 4] zip [sum] map')
In [47]:
J('23 9 +')
In [48]:
J('23 9 -')
In [49]:
J('23 9 *')
In [50]:
J('23 9 /')
In [51]:
J('23 -9 truediv')
In [52]:
J('23 9 div')
In [53]:
J('23 9 floordiv')
In [54]:
J('23 -9 div')
In [55]:
J('23 -9 floordiv')
In [56]:
J('23 9 %')
In [57]:
J('23 neg -5 neg')
In [58]:
J('2 10 pow')
In [59]:
J('23 sqr')
In [60]:
J('23 sqrt')
In [61]:
J('1 ++')
In [62]:
J('1 --')
In [63]:
J('8 1 <<')
In [64]:
J('8 1 >>')
In [65]:
J('[1 2 3 5] average')
In [66]:
J('5 range')
In [67]:
J('5 range_to_zero')
In [68]:
J('5 down_to_zero')
In [69]:
J('[1 2 3 5] product')
In [70]:
J('[1 2 3 5] sum')
In [71]:
J('[1 2 3 5] min')
In [72]:
J('45 30 gcd')
In [73]:
J('[45 30] least_fraction')
In [74]:
J('[23 12] least_fraction')
In [75]:
J('23 truthy')
In [76]:
J('[] truthy') # Python semantics.
In [77]:
J('0 truthy')
? == dup truthy
In [78]:
V('23 ?')
In [79]:
J('[] ?')
In [80]:
J('0 ?')
In [81]:
J('23 9 &')
In [82]:
J('23 9 !=')
The usual suspects:
<
lt
<=
le
=
eq
>
gt
>=
ge
not
or
In [83]:
J('1 1 ^')
In [84]:
J('1 0 ^')
In [85]:
J('[help] help')
In [86]:
J('[parse] help')
In [87]:
J('1 "2 [3] dup" parse')
In [88]:
J('[1 2 dup + +] run')
In [89]:
J('[app1] help')
In [90]:
J('10 4 [sqr *] app1')
In [91]:
J('10 3 4 [sqr *] app2')
In [92]:
J('[app2] help')
In [93]:
J('10 2 3 4 [sqr *] app3')
In [94]:
J('3 [0 <=] [1 - dup] anamorphism')
In [95]:
J('3 4 1 [+] [*] branch')
In [96]:
J('3 4 0 [+] [*] branch')
cleave
... x [P] [Q] cleave
From the original Joy docs: "The cleave combinator expects two quotations, and below that an item x
It first executes [P]
, with x
on top, and saves the top result element.
Then it executes [Q]
, again with x
, and saves the top result.
Finally it restores the stack to what it was below x
and pushes the two
results P(X) and Q(X)."
Note that P
and Q
can use items from the stack freely, since the stack (below x
) is restored. cleave
is a kind of parallel primitive, and it would make sense to create a version that uses, e.g. Python threads or something, to actually run P
and Q
concurrently. The current implementation of cleave
is a definition in terms of app2
:
cleave == [i] app2 [popd] dip
In [97]:
J('10 2 [+] [-] cleave')
In [98]:
J('1 2 3 4 5 [+] dip')
In [99]:
J('1 2 3 4 5 [+] dipd')
In [100]:
J('1 2 3 4 5 [+] dipdd')
In [101]:
V('23 [++] dupdip *') # N(N + 1)
In [102]:
J('[genrec] help')
In [103]:
J('3 [1 <=] [] [dup --] [i *] genrec')
In [104]:
V('1 2 3 [+ +] i')
In [105]:
J('1 2 [1] [+] [*] ifte')
In [106]:
J('1 2 [0] [+] [*] ifte')
In [107]:
V('1 2 3 [4 5 6] [* +] infra')
In [108]:
J('[loop] help')
In [109]:
V('3 dup [1 - dup] loop')
In [110]:
J('10 [1 2 3] [*] map')
In [111]:
J('10 5 [[*][/][+][-]] pam')
nullary
unary
binary
ternary
Run a quoted program enforcing arity.
In [112]:
J('1 2 3 4 5 [+] nullary')
In [113]:
J('1 2 3 4 5 [+] unary')
In [114]:
J('1 2 3 4 5 [+] binary') # + has arity 2 so this is technically pointless...
In [115]:
J('1 2 3 4 5 [+] ternary')
In [116]:
J('[step] help')
In [117]:
V('0 [1 2 3] [+] step')
In [118]:
V('3 2 1 2 [+] times')
In [119]:
J('[b] help')
In [120]:
V('1 2 [3] [4] b')
In [121]:
J('3 [0 >] [dup --] while')
In [122]:
J('[x] help')
In [123]:
V('1 [2] [i 3] x') # Kind of a pointless example.
void
Implements Laws of Form arithmetic over quote-only datastructures (that is, datastructures that consist soley of containers, without strings or numbers or anything else.)
In [124]:
J('[] void')
In [125]:
J('[[]] void')
In [126]:
J('[[][[]]] void')
In [127]:
J('[[[]][[][]]] void')